/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.suli.check.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.xingshuangs.iot.protocol.s7.serializer.S7Serializer;
import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.entity.Result;
import me.zhengjie.suli.check.domain.CheckDetail;
import me.zhengjie.suli.check.domain.CheckRecord;
import me.zhengjie.suli.check.domain.Checks;
import me.zhengjie.suli.check.repository.CheckDetailRepository;
import me.zhengjie.suli.check.repository.CheckRecordRepository;
import me.zhengjie.suli.check.repository.ChecksRepository;
import me.zhengjie.suli.check.service.ChecksService;
import me.zhengjie.suli.check.service.dto.ChecksDto;
import me.zhengjie.suli.check.service.dto.ChecksQueryCriteria;
import me.zhengjie.suli.check.service.mapstruct.ChecksMapper;
import me.zhengjie.suli.document.domain.Tray;
import me.zhengjie.suli.document.repository.TrayRepository;
import me.zhengjie.suli.inventory.domain.Inventory;
import me.zhengjie.suli.inventory.domain.InventoryDetail;
import me.zhengjie.suli.inventory.repository.InventoryDetailRepository;
import me.zhengjie.suli.inventory.repository.InventoryRepository;
import me.zhengjie.suli.tasks.domain.TaskDetail;
import me.zhengjie.suli.tasks.domain.Tasks;
import me.zhengjie.suli.tasks.repository.TaskDetailRepository;
import me.zhengjie.suli.tasks.repository.TasksRepository;
import me.zhengjie.suli.toHouse.service.ReceiptService;
import me.zhengjie.suli.wcs.domain.AgvTaskDetail;
import me.zhengjie.suli.wcs.domain.AgvTasks;
import me.zhengjie.suli.wcs.repository.AgvTaskDetailRepository;
import me.zhengjie.suli.wcs.repository.AgvTasksRepository;
import me.zhengjie.suli.wcs.domain.AgvTaskDetail;
import me.zhengjie.suli.wcs.domain.AgvTasks;
import me.zhengjie.suli.wcs.repository.AgvTaskDetailRepository;
import me.zhengjie.suli.wcs.repository.AgvTasksRepository;
import me.zhengjie.util.CommonUtil;
import me.zhengjie.util.UuidUtil;
import me.zhengjie.util.Variable;
import me.zhengjie.utils.*;
import me.zhengjie.utils.*;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Allen Gao
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2023-02-09
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class ChecksServiceImpl implements ChecksService {

    private final ChecksRepository checksRepository;
    private final ChecksMapper checksMapper;
    private final InventoryRepository inventoryRepository;
    private final TasksRepository tasksRepository;
    private final TaskDetailRepository taskDetailRepository;
    private final ReceiptService receiptService;
    private final TrayRepository trayRepository;

    @Autowired
    private CommonUtil commonUtil;
    @Autowired
    private CheckDetailRepository checkDetailRepository;
    @Autowired
    private InventoryDetailRepository inventoryDetailRepository;
    @Autowired
    private CheckRecordRepository checkRecordRepository;
    @Autowired
    private AgvTasksRepository agvTasksRepository;
    @Autowired
    private AgvTaskDetailRepository agvTaskDetailRepository;

    @Value("${agv.port}")
    private String agvPort;

    @Value("${agv.ip}")
    private String agvIp;

    @Override
    public Map<String, Object> queryAll(ChecksQueryCriteria criteria, Pageable pageable) {
        Page<Checks> page = checksRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(checksMapper::toDto));
    }

    @Override
    public List<ChecksDto> queryAll(ChecksQueryCriteria criteria) {
        return checksMapper.toDto(checksRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    @Transactional
    public ChecksDto findById(String id) {
        Checks checks = checksRepository.findById(id).orElseGet(Checks::new);
        ValidationUtil.isNull(checks.getId(), "Checks", "id", id);
        return checksMapper.toDto(checks);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChecksDto create(Checks resources) {
        resources.setId(IdUtil.simpleUUID());
        return checksMapper.toDto(checksRepository.save(resources));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Checks resources) {
        Checks checks = checksRepository.findById(resources.getId()).orElseGet(Checks::new);
        ValidationUtil.isNull(checks.getId(), "Checks", "id", resources.getId());
        checks.copy(resources);
        checksRepository.save(checks);
    }

    @Override
    public void deleteAll(String[] ids) {
        for (String id : ids) {
            checksRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<ChecksDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (ChecksDto checks : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("盘点单号", checks.getCheckPlanCode());
            map.put("所属仓库", checks.getWarehouseCode());
            map.put("状态", checks.getStatus());
            map.put("库位", checks.getLocationCode());
            map.put("载具", checks.getTrayCode());
            map.put("物料", checks.getMaterialCode());
            map.put("批次", checks.getLotCode());
            map.put("单位", checks.getUnitCode());
            map.put("创建用户", checks.getCreateBy());
            map.put("修改用户", checks.getUpdateBy());
            map.put("创建日期", checks.getCreateTime());
            map.put("修改日期", checks.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int generateCheckTasks(String param) {
        JSONObject jsonObject = JSON.parseObject(param);
        JSONArray jsonArray1 = jsonObject.getJSONArray("selection1");    // 1级表格
        JSONArray jsonArray2 = jsonObject.getJSONArray("selection2");   // 2级表格
        JSONArray jsonArray3 = jsonObject.getJSONArray("selection3");   // 3级表格
        /** 如果1级表格中有值，就查询所有1级表的库存，包含2，3级 */
        Set<String> inventoryIdSet = new HashSet<>();
        for (int i = 0; i < jsonArray1.size(); i++) {
            JSONObject jsonObjectLevel1 = jsonArray1.getJSONObject(i);
            List<Inventory> inventoryList = inventoryRepository.findByMaterialCode(jsonObjectLevel1.getString("material_code"));
            for (Inventory inventory : inventoryList) {
                inventoryIdSet.add(inventory.getId());
            }
        }

        /** 2级表格，按批次号筛选 */
        for (int i = 0; i < jsonArray2.size(); i++) {
            JSONObject jsonObjectLevel2 = jsonArray2.getJSONObject(i);
            String materialCode = jsonObjectLevel2.getString("material_code");
            String lotCode = jsonObjectLevel2.getString("lot_code");
            List<Inventory> inventoryList = inventoryRepository.findByLotCodeAndElNo(lotCode, materialCode);
            for (Inventory inventory : inventoryList) {
                inventoryIdSet.add(inventory.getId());
            }
        }

        /** 3级菜单 */
        for (int i = 0; i < jsonArray3.size(); i++) {
            JSONObject jsonObjectLevel3 = jsonArray3.getJSONObject(i);
            String id = jsonObjectLevel3.getString("id");
            inventoryIdSet.add(id);
        }

        for (String id : inventoryIdSet) {
            Inventory inventory = inventoryRepository.findById(id).orElseGet(null);

            if (null == inventory) {
                return 2;   // 库存不存在
            }
            if (inventory.getIsHold() == 1) {   // 对于已冻结的物料
                return 3;   // 库存已冻结
            }else if (inventory.getIsHold() == 2){
                return 3;   // 库存已冻结
            }


            /** 生成抽检记录 */
            CheckRecord checkRecord = new CheckRecord();
            checkRecord.setId(IdUtil.simpleUUID());
            checkRecord.setCheckCode(UuidUtil.genCode());
            checkRecord.setCheckStatus(1);  // 1--抽检出库中
            checkRecord.setMaterialCode(inventory.getMaterialCode());
            checkRecord.setMaterialName(inventory.getMaterialName());
            checkRecord.setUnit(inventory.getElUnit());
            checkRecord.setPlanQty(inventory.getTotal());
            checkRecord.setCheckQty(null);
            checkRecord.setDiffQty(null);
            checkRecord.setTrayCode(inventory.getTrayCode());
            checkRecord.setLocationCode(inventory.getWarehouseLocation());
            checkRecord.setLotCode(inventory.getLotCode());
            checkRecord.setPkList(inventory.getBoxNumber());
            checkRecord.setGrade(inventory.getGrade());
            checkRecord.setVmiCode(inventory.getVmiCode());
            checkRecord.setSuNo(inventory.getSuNo());
            checkRecord.setChSeq(inventory.getChSeq());
            checkRecord.setSourceCode(inventory.getId());
            checkRecordRepository.save(checkRecord);

            /** 生成抽检出库任务 */
            Tasks tasks = new Tasks();
            tasks.setId(IdUtil.simpleUUID());
            tasks.setTaskType(5);   /// 盘点出库
            tasks.setStatus(1); // 1--执行中
            tasks.setTaskCode(UuidUtil.genCode());
            tasks.setElNo(inventory.getMaterialCode());
            tasks.setElName(inventory.getMaterialName());
            tasks.setTrayCode(inventory.getTrayCode());
            tasks.setMtQty(inventory.getTotal());
            if (commonUtil.isStringNotEmpty(inventory.getWarehouseLocation())) {
                tasks.setLocationFrom(inventory.getWarehouseLocation());
            } else {
                tasks.setLocationFrom(inventory.getLocationCode());
            }
            tasks.setElUnit(inventory.getElUnit());
            tasks.setStartTime(new Timestamp(new Date().getTime()));
            tasks.setSourceCode(checkRecord.getCheckCode());
            tasksRepository.save(tasks);

            /** 冻结库存并修改冻结数量 */
            inventory.setHoldQuantity(inventory.getAvailableQuantity());
            inventory.setAvailableQuantity(new BigDecimal(0));
            inventory.setIsHold(1);
            inventoryRepository.save(inventory);


            /** 生成AGV的出库任务 */
            TaskDetail taskDetail = new TaskDetail();
            taskDetail.setId(IdUtil.simpleUUID());
            taskDetail.setTaskCode(UuidUtil.genCode());
            taskDetail.setBoxNumber(inventory.getBoxNumber());
            taskDetail.setPkList(inventory.getBoxNumber());
            taskDetail.setPkQty(inventory.getTotal());
            if (commonUtil.isStringNotEmpty(inventory.getWaNo())) {
                taskDetail.setWaNo(inventory.getWaNo());
            }
            if (commonUtil.isStringNotEmpty(inventory.getSuNo())) {
                taskDetail.setSuNo(inventory.getSuNo());
            }
            if (commonUtil.isStringNotEmpty(inventory.getVmiCode())) {
                taskDetail.setVmiCode(inventory.getVmiCode());
            }
            taskDetail.setTaskId(tasks.getId());
            taskDetail.setLotNo(inventory.getLotCode());
            taskDetailRepository.save(taskDetail);

            /** 创建AGV任务列表 */
            AgvTasks agvTasks = new AgvTasks();
            agvTasks.setId(IdUtil.simpleUUID());
            agvTasks.setTaskGroupCode(UuidUtil.genCode());
            agvTasks.setBillNumber(tasks.getTaskCode());    // 源单编号，来自于WMS中抽检出库任务的TaskCode
            agvTasks.setTaskType("抽检出库");
            agvTasks.setStatus(1);  // 1--执行中
            agvTasks.setGroupPriority(0);
            agvTasksRepository.save(agvTasks);

            /** AGV任务明细 */
            AgvTaskDetail agvTaskDetail = new AgvTaskDetail();
            agvTaskDetail.setId(IdUtil.simpleUUID());
            agvTaskDetail.setTaskCode(UuidUtil.genCode());
            agvTaskDetail.setStatus(1); // 1--执行中
            agvTaskDetail.setTaskPriority(0);
            agvTaskDetail.setContainerCode(inventory.getTrayCode());
            agvTaskDetail.setContainerType("CT_KUBOT_STANDARD");
            agvTaskDetail.setFromLocationCode(inventory.getWarehouseLocation());
            agvTaskDetail.setToStationCode("CY01");
            agvTaskDetail.setSourceCode(taskDetail.getTaskCode());
            agvTaskDetail.setMainId(agvTasks.getId());
            agvTaskDetailRepository.save(agvTaskDetail);

            /** 给AGV发送出库任务 */
            JSONObject jsonSendToAgv = new JSONObject();
            jsonSendToAgv.put("taskType", "tote_outbound");
//            jsonSendToAgv.put("groupPriority", 1);
            jsonSendToAgv.put("taskGroupCode", UuidUtil.genCode());

            JSONObject taskObject = new JSONObject();
            taskObject.put("taskCode", agvTaskDetail.getTaskCode());
            taskObject.put("taskPriority", 100);

            List<JSONObject> taskDescribeList = new ArrayList<>();

            JSONObject taskDescribe = new JSONObject();
            taskDescribe.put("containerCode", inventory.getTrayCode());
            taskDescribe.put("storageTag", "");
            taskDescribe.put("fromLocationCode", inventory.getWarehouseLocation());
            List<String> toLocationCode = new ArrayList<>();
            toLocationCode.add("CY01");
            taskDescribe.put("toStationCode", toLocationCode);

            taskObject.put("taskDescribe", taskDescribe);
            taskDescribeList.add(taskObject);

            jsonSendToAgv.put("tasks", taskDescribeList);

            String s = commonUtil.httpPost("http://" + agvIp + ":" + agvPort + "/task/create", jsonSendToAgv.toJSONString());
            Result result = JSON.parseObject(s, Result.class);
            log.info("抽检发货发送任务收到agv反馈" + result);
            if (result.getCode() != 0) {
                log.info("抽检发货发送任务时异常，原因：" + result);
                return 0;
            }
        }
        return 1;
    }

    @Override
    public List<Map<String, Object>> pdaGetOneCheckTaskByTrayName(String trayName) {
        List<Map<String, Object>> checkDetailList = checksRepository.pdaGetOneCheckByTrayName(trayName);
        return checkDetailList;
    }

    @Override
    public String addCheckByLotCode(String param) {
        List<Inventory> inventories = inventoryRepository.findByLotCode(param);
        Checks checks = new Checks();
        checks.setId(UuidUtil.genCode());
        checks.setMaterialCode(param);
        checks.setCheckPlanCode(UuidUtil.genUuid().toString());
        checks.setWarehouseCode("智能立体仓");
        checks.setStatus(1);
        checks.setUnitCode(inventories.get(0).getElUnit());
        Snowflake snowflake = new Snowflake();
        List<CheckDetail> checkDetailList = new ArrayList<>();
        for (Inventory inventory : inventories) {
            CheckDetail checkDetail = new CheckDetail();
            checkDetail.setId(snowflake.nextIdStr());
            checkDetail.setCheckId(checks.getId());
            checkDetail.setCheckDetailCode(UuidUtil.genCode());
            checkDetail.setLotCode(inventory.getLotCode());
            checkDetail.setQty(inventory.getAvailableQuantity());
            checkDetail.setCreateBy("admin");
            checkDetail.setCreateTime(new Timestamp(new Date().getTime()));
            checkDetail.setMaterialCode(inventory.getMaterialCode());
            checkDetail.setMaterialName(inventory.getMaterialName());
            checkDetail.setUnitCode(inventory.getElUnit());
            checkDetail.setBoxNumber(inventory.getBoxNumber());
            checkDetailList.add(checkDetail);
        }
        checksRepository.save(checks);
        checkDetailRepository.saveAll(checkDetailList);
        return "success";
    }

    @Override
    public String doCheck(String param) {
        Checks check = JSON.parseObject(param, Checks.class);
        JSONObject agvTask = new JSONObject();
        agvTask.put("taskType", "tote_outbound");
//        agvTask.put("groupPriority", 0);
        List<JSONObject> tasks = new ArrayList<>();
        List<CheckDetail> checkDetailList = checkDetailRepository.getCheckDetailsByCheckId(check.getId());
        List<String> collect = checkDetailList.stream().map(CheckDetail::getLotCode).collect(Collectors.toList());
        List<Inventory> inventoryList = inventoryRepository.findByLotCodes(collect);
        for (CheckDetail checkDetail : checkDetailList) {
            JSONObject task = new JSONObject();
            task.put("taskCode", checkDetail.getCheckDetailCode());
            task.put("taskPriority", 0);
            JSONObject taskDescribe = new JSONObject();
            taskDescribe.put("containerCode", checkDetail.getTrayCode());
            if (SecurityUtils.getCurrentUsername().equals("admin")) {
                List<String> list = new ArrayList<>();
                list.add("CY01");
                taskDescribe.put("toLocationCode", list);
            } else {
                List<String> list = new ArrayList<>();
                list.add("CY02");
                taskDescribe.put("toLocationCode", list);
            }
            task.put("taskDescribe", taskDescribe);
            tasks.add(task);
        }
        String s = commonUtil.httpPost("http://" + agvIp + ":" + agvPort + "/task/create", JSON.toJSONString(tasks));
        log.info("盘点任务发送：" + JSON.toJSONString(tasks));
        Result result = JSON.parseObject(s, Result.class);
        log.info("收到agv反馈" + result);
        if (result.getCode() != 0) {
            log.info("agv响应异常，原因：" + result);
            return "盘点单号为：" + check.getCheckPlanCode() + "的订单agv响应异常";
        }
        return "success";
    }

    @Override
    public String updateQTY(String param) {
        if (null == param || param.equals("")) {
            return "fail";
        }
        List<JSONObject> jsonObjects = JSON.parseArray(param, JSONObject.class);
        for (JSONObject jsonObject : jsonObjects) {
            Inventory inventory1 = inventoryRepository.findByBoxNumber(jsonObject.getString("pk_list"));
            if (null == inventory1) {
                Inventory inventory = new Inventory();
                inventory.setId(IdUtil.simpleUUID());
                inventory.setMaterialCode(jsonObject.getString("el_no"));
                inventory.setMaterialName(jsonObject.getString("el_name"));
                inventory.setLotCode(jsonObject.getString("lot_no"));
                inventory.setAvailableQuantity(jsonObject.getBigDecimal("mt_qty"));
                inventory.setHoldQuantity(new BigDecimal(0));
                inventory.setTotal(jsonObject.getBigDecimal("mt_qty"));
                inventory.setGrade("0");
                inventory.setWarehouseLocation("暂存区");
                inventory.setLocationCode("暂存区");
                inventory.setBoxNumber(jsonObject.getString("pk_list"));
                inventory.setIsHold(0);
                inventory.setStartDate(new Timestamp(new Date().getTime()));
                inventory.setTrayCode("");
                inventory.setZoneCode(jsonObject.getString("mt_area")); // 所属库区
//                inventory.setSuNo(jsonObject.getString("pk_list"));   // 供应商
                inventory.setElUnit(jsonObject.getString("el_unit"));   // 单位
                inventoryRepository.save(inventory);
            } else {
                inventory1.setAvailableQuantity(jsonObject.getBigDecimal("mt_qty"));
                inventoryRepository.save(inventory1);
            }
        }
        return "success";
    }
}